/******************************************************************
*
* CyberHTTP for Java
*
* Copyright (C) Satoshi Konno 2002
*
* File: HTTPStatus.java
*
* Revision;
*
* 12/17/02
*   - first revision.
* 09/03/03
*   - Added CONTINUE_STATUS.
* 
******************************************************************/

package com.cidero.http;

import java.util.StringTokenizer;
import java.util.logging.Logger;

/**
 * Simple HTTP status class.  HTTP status is contained in the first
 * line of every HTTP response. Syntax of the status line is:
 *  
 *  <version> <statusCode> <reasonString>
 *
 * Examples are:
 *
 *  HTTP/1.0 200 OK
 *  HTTP/1.0 404 Not Found
 */
public class HTTPStatus 
{
  private static Logger logger = Logger.getLogger("com.cidero.http");

  ////////////////////////////////////////////////
  //  Status Codes
  ////////////////////////////////////////////////
  
  // 100-199: Informational status codes
  public static final int CONTINUE = 100;
	public static final int SWITCHING_PROTOCOLS = 101;

  // 200-299: Success status codes
  public static final int OK = 200;
	public static final int CREATED = 201;
	public static final int ACCEPTED = 202;
	public static final int NON_AUTHORITATIVE_INFORMATION = 203;
	public static final int NO_CONTENT = 204;
	public static final int RESET_CONTENT = 205;
	public static final int PARTIAL_CONTENT = 206;

  // 300-307  URL Redirection (mostly)
	public static final int MULTIPLE_CHOICES = 300;
	public static final int MOVED_PERMANENTLY = 301;
	public static final int FOUND = 302;
	public static final int SEE_OTHER = 303;
	public static final int NOT_MODIFIED = 304;
	public static final int USE_PROXY = 305;
	public static final int TEMPORARY_REDIRECT = 307;

  // 400-499: Client error status codes
	public static final int BAD_REQUEST = 400;
	public static final int UNAUTHORIZED = 401;
	public static final int PAYMENT_REQUIRED = 402;
	public static final int FORBIDDEN = 403;
	public static final int NOT_FOUND = 404;
	public static final int METHOD_NOT_ALLOWED = 405;
	public static final int NOT_ACCEPTABLE = 406;
	public static final int PROXY_AUTHENTICATION_REQUIRED = 407;
	public static final int REQUEST_TIMEOUT = 408;
	public static final int CONFLICT = 409;
	public static final int GONE = 410;
	public static final int LENGTH_REQUIRED = 411;
	public static final int PRECONDITION_FAILED = 412;
	public static final int REQUEST_ENTITY_TOO_LARGE = 413;
	public static final int REQUEST_URI_TOO_LONG = 414;
	public static final int UNSUPPORTED_MEDIA_TYPE = 415;
	public static final int REQUESTED_RANGE_NOT_SATISFIABLE = 416;
	public static final int EXPECTATION_FAILED = 417;

  // 500-599: Server error status codes
	public static final int INTERNAL_SERVER_ERROR = 500;
	public static final int NOT_IMPLEMENTED = 501;
	public static final int BAD_GATEWAY = 502;
	public static final int SERVICE_UNAVAILABLE = 503;
	public static final int GATEWAY_TIMEOUT = 504;
	public static final int HTTP_VERSION_NOT_SUPPORTED = 505;


  /*
  public static final int BAD_REQUEST = 400;
  public static final int NOT_FOUND = 404;
  public static final int INTERNAL_SERVER_ERROR = 500;

  public static final int INVALID_ACTION = 401;
  public static final int INVALID_ARGS = 402;
  public static final int OUT_OF_SYNC = 403;
  public static final int INVALID_VAR = 404;
  public static final int PRECONDITION_FAILED = 412;
  public static final int ACTION_FAILED = 501;
  */

  private String version = "";
  private int    statusCode = 0;
  private String reasonPhrase = "";


  /**
   * Return the string version of an HTTP status code
   *
   * @return  
   */
  public static final String code2String(int code)
  {
    switch (code) {

      //
      // List most-oft used once first (small optimization)
      //

      // 100's
      case CONTINUE: return "Continue";
      // 200's
      case OK: return "OK";
      // 400's
      case BAD_REQUEST: return "Bad Request";
      case NOT_FOUND: return "Not Found";
      case PRECONDITION_FAILED: return "Precondition Failed";
      // 500's
      case INTERNAL_SERVER_ERROR: return "Internal Server Error";

      //
      // Now the rarer ones
      //

      // 100's
      case SWITCHING_PROTOCOLS: return "Switching Protocols";

      // 200's
      case CREATED: return "Created";
      case ACCEPTED: return "Accepted";
      case NON_AUTHORITATIVE_INFORMATION: return "Non Authoritative Information";
      case NO_CONTENT: return "No Content";
      case RESET_CONTENT: return "Reset Content";
      case PARTIAL_CONTENT: return "Partial Content";

      // 300's
      case MULTIPLE_CHOICES: return "Multiple Choices";
      case MOVED_PERMANENTLY: return "Moved Permanently";
      case FOUND: return "Found";
      case SEE_OTHER: return "See Other";
      case NOT_MODIFIED: return "Not Modified";
      case USE_PROXY: return "Use Proxy";
      case TEMPORARY_REDIRECT: return "Temporary Redirect";

      // 400's
      case UNAUTHORIZED: return "Unauthorized";
      case PAYMENT_REQUIRED: return "Payment Required";
      case FORBIDDEN: return "Forbidden";
      case METHOD_NOT_ALLOWED: return "Method Not Allowed";
      case NOT_ACCEPTABLE: return "Not Acceptable";
      case PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
      case REQUEST_TIMEOUT: return "Request Timeout";
      case CONFLICT: return "Conflict";
      case GONE: return "Gone";
      case LENGTH_REQUIRED: return "Length Required";
      case REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
      case REQUEST_URI_TOO_LONG: return "Request URI Too Long";
      case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
      case REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
      case EXPECTATION_FAILED: return "Expectation Failed";

      // 500's
      case NOT_IMPLEMENTED: return "Not Implemented";
      case BAD_GATEWAY: return "Bad Gateway";
      case SERVICE_UNAVAILABLE: return "Service Unavailable";
      case GATEWAY_TIMEOUT: return "Gateway Timeout";
      case HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";

      default: return "Unknown Code (" + code + ")";
    }
  }
  
  ////////////////////////////////////////////////
  //  Constructor
  ////////////////////////////////////////////////

  public HTTPStatus()
  {
    setVersion("");
    setStatusCode(0);
    setReasonPhrase("");
  }
  
  public HTTPStatus(String ver, int code, String reason)
  {
    setVersion(ver);
    setStatusCode(code);
    setReasonPhrase(reason);
  }

  public HTTPStatus(String lineStr) {
    set(lineStr);
  }
  
  ////////////////////////////////////////////////
  //  Member
  ////////////////////////////////////////////////

  public void setVersion(String value) {
    version = value;
  }
  
  public void setStatusCode(int value) {
    statusCode = value;
  }
  
  public void setReasonPhrase(String value) {
    reasonPhrase = value;
  }
  
  public String getVersion() {
    return version;
  }
  
  public int getStatusCode() {
    return statusCode;
  }
  
  public String getReasonPhrase() {
    return reasonPhrase;
  }

  /**
   * Set the fields of the status object by parsing the first line of 
   * HTTP response
   *
   * @param  lineStr    First line of HTTP response
   */
  public void set(String lineStr)
  {
    if (lineStr == null) {
      setVersion( HTTP.VERSION );
      setStatusCode( INTERNAL_SERVER_ERROR );
      setReasonPhrase(code2String( INTERNAL_SERVER_ERROR ));
      return;
    }

    try {
      StringTokenizer st = new StringTokenizer(lineStr,
                                               HTTP.STATUS_LINE_DELIM);
      // Get version
      if (st.hasMoreTokens() == false) {
        logger.warning("Error - no version field found in HTTP response");
        return;
      }
      version = st.nextToken().trim();

      // Get status code
      if (st.hasMoreTokens() == false) {
        logger.warning("Error - no status code field found in HTTP response");
        return;
      }
      String codeStr = st.nextToken();
      int code = 0;
      try {
        code = Integer.parseInt(codeStr);
      } catch (NumberFormatException e1) {
        logger.warning("Error - non-numerical status code found");
      }
      statusCode = code;

      if (st.hasMoreTokens() == false) {
        logger.warning("Error - no reason code field found in HTTP response");
        return;
      }
      reasonPhrase = st.nextToken().trim();

    }
    catch (Exception e) {
      logger.warning(e.toString());
    }

  }
  
}
